home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Graphics / PostScript / Pencil / Source / PencilGraphic.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  22.0 KB  |  791 lines

  1. /*
  2. Pencil V1.0, Copyright 1994, 95 by Florian Marquardt.
  3. This program may be distributed under the terms of the GNU general
  4. public license (Start Pencil and select "Info>COPYING..." for a copy of the
  5. license).
  6. */
  7. #import "PencilGraphic.h"
  8. #import "PencilView.h"
  9. #import <math.h>
  10.  
  11. #define ADJUST [view convertPoint:&te->location fromView:nil]
  12. #define EX te->location.x
  13. #define EY te->location.y
  14.  
  15. #define M_180_PI 180.0/M_PI
  16.  
  17. extern float athresh, sumthresh;
  18. extern float *fpts;
  19. extern int maxfpts;
  20.  
  21. extern NXStream *result;
  22.  
  23. extern double fabs(double x);
  24.  
  25. BOOL globalOutlines=NO;
  26.  
  27. float angle(float x1,float y1,float x2,float y2)
  28. {
  29.     return atan2(-y1*x2+x1*y2,x1*x2+y1*y2);
  30. }
  31.  
  32. void expandRect (NXRect *re, float x, float y)
  33. {
  34.     if(x<re->origin.x)    { re->size.width+=re->origin.x-x; re->origin.x=x; }
  35.     if(x>re->origin.x+re->size.width)    { re->size.width=x-re->origin.x; }
  36.     if(y<re->origin.y)    { re->size.height+=re->origin.y-y; re->origin.y=y; }
  37.     if(y>re->origin.y+re->size.height)    { re->size.height=y-re->origin.y; }
  38. }
  39.  
  40. #define TRANSFORMX(a,b) (tx+sx*cos(phi)*(a)-sy*sin(phi)*(b))
  41. #define TRANSFORMY(a,b) (ty+sx*sin(phi)*(a)+sy*cos(phi)*(b))
  42.  
  43.  
  44. void read_brace_string(NXStream *stream, char *to)
  45. {
  46.     int open=0;
  47.     char a;
  48.     int shouldLoop=1;
  49.     
  50.     *to=0;
  51.     
  52.     NXScanf(stream, " %*[^{]{");
  53.     do {
  54.         NXScanf(stream, "%[^{}]", to);
  55.         to+=strlen(to);
  56.         if((a=NXGetc(stream))=='{') {
  57.             ++open; *(to++)='{';
  58.         } else {
  59.             if(a=='}') {
  60.                 --open;
  61.                 if(open<0) shouldLoop=0; else *(to++)='}';
  62.             }
  63.             else
  64.                 if(a<=0)    shouldLoop=0;
  65.         }
  66.     } while(shouldLoop);
  67. }
  68.  
  69. @implementation PencilGraphic
  70. + initialize
  71. {
  72.     [self setVersion:1];
  73.     return self;
  74. }
  75.  
  76. - (void)transformPoint:(NXPoint *)pt
  77. {
  78.     float xh, yh;
  79.  
  80.     if(transformed)
  81.     {
  82.     xh=pt->x-tx;
  83.     yh=pt->y-ty;
  84.     pt->x=(xh*cos(phi)+yh*sin(phi))/sx;
  85.     pt->y=(-xh*sin(phi)+yh*cos(phi))/sy;
  86.     }
  87. }
  88.  
  89. - (void)rotateAroundCenter:(float)x:(float)y fromPoint:(NXPoint *)pt1 toPoint:(NXPoint *)pt2
  90. {
  91.     float dphi, dx, dy;
  92.  
  93.     dphi=angle(pt1->x-x,pt1->y-y,pt2->x-x,pt2->y-y);
  94.     phi+=dphi;
  95.     dx=tx-x;
  96.     dy=ty-y;
  97.     tx=x+cos(dphi)*dx-sin(dphi)*dy;
  98.     ty=y+sin(dphi)*dx+cos(dphi)*dy;
  99.     if(fabs(phi)<=1e-6) { phi=0; rotated=NO; } else { rotated=YES; }
  100.     if(hypot(tx,ty)<=1e-6) { tx=ty=0; translated=NO; } else { translated=YES; }
  101.     transformed=(translated || scaled || rotated) ? YES:NO;
  102. }
  103.  
  104. - (void)scaleCenter:(float)cx:(float)cy by:(float)scx:(float)scy
  105. {
  106.     NXPoint c;
  107.     
  108.     c.x=cx;
  109.     c.y=cy;
  110.     [self transformPoint:&c];
  111.     tx=TRANSFORMX(c.x*(1-scx),c.y*(1-scy));
  112.     ty=TRANSFORMY(c.x*(1-scx),c.y*(1-scy));
  113.     sx*=scx;
  114.     sy*=scy;
  115.     if(fabs(sx-1.0)+fabs(sy-1.0)<1e-6) { sx=sy=1; scaled=NO; } else { scaled=YES; }
  116.     if(hypot(tx,ty)<=1e-6) { tx=ty=0; translated=NO; } else { translated=YES; }
  117.     transformed=(translated || scaled || rotated) ? YES:NO;
  118. }
  119.  
  120. - (BOOL)selected:(NXEvent *)te:(int *)cp:(id)view
  121. {
  122.     int val;
  123.     NXPoint pt;
  124.  
  125.     pt=te->location;
  126.     [view lockFocus];
  127.     [self transformPoint:&pt];
  128.     if(data)    DPSPrintf(DPSGetCurrentContext()," /udt { %s } def ", data); else DPSPrintf(DPSGetCurrentContext()," /udt {} def ");
  129.     DPSPrintf(DPSGetCurrentContext(), " /ctrl ");
  130.     if(controlpts) PSsendfloatarray(controlpts, 2*ncontrolpts); else DPSPrintf(DPSGetCurrentContext(), "[ ]");
  131.     DPSPrintf(DPSGetCurrentContext()," def /nctrl %d def /mmx %g def /mmy %g def /cp { %s } def /linw %g def %sSel\n", ncontrolpts, pt.x, pt.y, methodname, linewidth, drawingmethod);
  132.     PSgetboolean(&val);
  133.     [view unlockFocus];
  134.     return val;
  135. }
  136.  
  137. - (BOOL)move:(NXEvent *)tte:(int *)cp:(id)view:(float)bsize
  138. {
  139.         int    oldMask;
  140.     BOOL shouldLoop=YES;
  141.     int val;
  142.     NXEvent *te;
  143.     NXEvent mte;
  144.  
  145.     mte=*tte;
  146.     te=&mte;
  147.     [view lockFocus];
  148.     [self transformPoint:&te->location];
  149.     DPSPrintf(DPSGetCurrentContext(), " /ctrl ");
  150.     if(controlpts) PSsendfloatarray(controlpts, 2*ncontrolpts); else DPSPrintf(DPSGetCurrentContext(), "[ ]");
  151.     DPSPrintf(DPSGetCurrentContext()," def /nctrl %d def /mmx %g def /mmy %g def genselpt\n", ncontrolpts, EX, EY);
  152.     PSgetint(&val);
  153.     
  154.     if(val>=0)
  155.     {
  156.         float dx, dy;
  157.         NXEvent thisEvent;
  158.         
  159.         *cp=val;
  160.         oldMask = [[view window] addToEventMask:NX_LMOUSEDRAGGEDMASK];
  161.  
  162.     PSsetinstance(YES);
  163.     dx=controlpts[2**cp]-EX;
  164.     dy=controlpts[2**cp+1]-EY;
  165.     while (shouldLoop) {
  166.         do {
  167.     te = [NXApp getNextEvent:(NX_LMOUSEUPMASK |
  168.                                          NX_LMOUSEDRAGGEDMASK)];
  169.         } while(te->type==NX_LMOUSEDRAGGED && ([NXApp peekNextEvent:NX_LMOUSEDRAGGEDMASK into:&thisEvent]));
  170.  
  171.        ADJUST;
  172.     if(te->type==NX_LMOUSEUP)
  173.         shouldLoop=NO; 
  174.     else {
  175.     [self transformPoint:&te->location];
  176.     controlpts[2**cp]=EX+dx;
  177.     controlpts[2**cp+1]=EY+dy;
  178.     PSnewinstance();
  179.     [self drawControl:NULL:*cp:bsize];
  180.     PSflushgraphics();
  181.     NXPing();
  182.     }
  183.     }
  184.     PSsetinstance(NO);
  185.     [view unlockFocus];
  186.     [[view window] setEventMask:oldMask];
  187.         
  188.         return YES;
  189.     }
  190.     else
  191.     {
  192.         [view unlockFocus];
  193.         return NO;
  194.     }
  195. }
  196.  
  197. - create:(NXEvent *)tte:(int *)cp:(id)view:(float)bsize;
  198. {
  199.     int            oldMask;
  200.     BOOL shouldLoop=YES;
  201.     NXEvent *te;
  202.     NXEvent mte;
  203.     NXEvent thisEvent;
  204.     
  205.     mte=*tte;
  206.     te=&mte;
  207.  
  208.     oldMask = [[view window] addToEventMask:NX_LMOUSEDRAGGEDMASK | NX_RMOUSEDOWNMASK];
  209.  
  210.     ADJUST;
  211.     controlpts=(float *)malloc(sizeof(float)*2);
  212.     controlpts[0]=EX; controlpts[1]=EY;
  213.     ncontrolpts=1;
  214.     [view lockFocus];
  215.     PSsetinstance(YES);
  216.     [self drawControl:NULL:0:bsize];
  217.     PSflushgraphics();
  218.     while (shouldLoop) {
  219.             do {
  220.         te = [NXApp getNextEvent:(NX_LMOUSEUPMASK |
  221.                                          NX_LMOUSEDRAGGEDMASK | NX_RMOUSEDOWNMASK | NX_LMOUSEDOWNMASK)];
  222.         } while(te->type==NX_LMOUSEDRAGGED && ([NXApp peekNextEvent:NX_LMOUSEDRAGGEDMASK into:&thisEvent]));
  223.  
  224.  
  225.         switch (te->type) {
  226.         case NX_LMOUSEUP:
  227.     break;
  228.     case NX_LMOUSEDRAGGED:
  229.     ADJUST;
  230.     controlpts[2*(ncontrolpts-1)]=EX;
  231.     controlpts[2*(ncontrolpts-1)+1]=EY;
  232.     PSnewinstance();
  233.     [self drawControl:NULL:ncontrolpts-1:bsize];
  234.     PSflushgraphics();
  235.     NXPing();
  236.              break;
  237.         case NX_LMOUSEDOWN:
  238.     ADJUST;
  239.     ncontrolpts++;
  240.     controlpts=(float *)realloc(controlpts, sizeof(float)*2*ncontrolpts);
  241.     controlpts[2*(ncontrolpts-1)]=EX;
  242.     controlpts[2*(ncontrolpts-1)+1]=EY;
  243.     PSnewinstance();
  244.     [self drawControl:NULL:ncontrolpts-1:bsize];
  245.     PSflushgraphics();
  246.     NXPing();
  247.             break;
  248.     case NX_RMOUSEDOWN:
  249.     shouldLoop=NO;
  250.     *cp=ncontrolpts-1;
  251.     break;
  252.         default:
  253.             break;
  254.         }
  255.  
  256.     }
  257.     PSsetinstance(NO);
  258.     [view unlockFocus];
  259.     [[view window] setEventMask:oldMask];
  260.     return self;
  261. }
  262.  
  263. #define PX(i) fpts[(i)*2]
  264. #define PY(i) fpts[(i)*2+1]
  265. #define NLINMAX 1000
  266.  
  267. - createPolyFreehand:(NXEvent *)tte:(int *)cp:(id)view:(float)bsize
  268. {
  269.     int            oldMask;
  270.     BOOL shouldLoop=YES;
  271.     NXEvent *te;
  272.     NXEvent mte;
  273.     int n=1, nlin=0, i0=0, i, j;
  274.     float ax, ay, ttx, tty, tl, sum, amax, dx, dy, val;
  275.  
  276.     mte=*tte;
  277.     te=&mte;
  278.  
  279.     oldMask = [[view window] addToEventMask:NX_LMOUSEDRAGGEDMASK];
  280.  
  281.     ADJUST;
  282.  
  283.     controlpts=(float *)malloc(sizeof(float)*2);
  284.     controlpts[0]=EX; controlpts[1]=EY;
  285.     ncontrolpts=1;
  286.     [view lockFocus];
  287.     PSnewinstance();
  288.     PSsetinstance(YES);
  289.     PSsetgray(0);
  290.     PSrectfill(EX,EY,1,1);
  291.     PSflushgraphics();
  292.     PX(0)=EX; PY(0)=EY;
  293.     while (shouldLoop) {
  294.         te = [NXApp getNextEvent:(NX_LMOUSEUPMASK |
  295.                                          NX_LMOUSEDRAGGEDMASK)];
  296.  
  297.     ADJUST;
  298.     PSrectfill(EX,EY,1,1);
  299.     PSflushgraphics();
  300.     if(n>=maxfpts)    { fpts=(float *)realloc(fpts, sizeof(float)*2*(maxfpts*=2)); }
  301.     PX(n)=EX; PY(n)=EY; ++n;
  302.     if(te->type==NX_LMOUSEUP)
  303.         shouldLoop=NO;
  304.         }
  305.     PSsetinstance(NO);
  306.     [view unlockFocus];
  307.     [[view window] setEventMask:oldMask];
  308.  
  309.     controlpts=(float *)malloc(sizeof(float)*2);
  310.     controlpts[0]=PX(0); controlpts[1]=PY(0);
  311.     ncontrolpts=1;
  312.  
  313.     --n;
  314.  
  315.     while(++nlin<=NLINMAX && i0<n-1)
  316.     {
  317.         ax=PX(i0);
  318.         ay=PY(i0);
  319.  
  320.         for(i=i0+2;i<=n;i++)    {
  321.         ttx=PX(i)-ax;
  322.         tty=PY(i)-ay;
  323.         tl=hypot(ttx,tty);
  324.         ttx/=tl;
  325.         tty/=tl;
  326.         sum=0;
  327.         amax=0;
  328.         for(j=i0+1;j<=i-1;j++)    {
  329.         dx=PX(j)-ax;
  330.         dy=PY(j)-ay;
  331.         val=ttx*dy-tty*dx;
  332.         sum+=val;
  333.         val=fabs(val);
  334.         if(val>amax)    amax=val;
  335.         }
  336.         if(amax>athresh && fabs(sum/(i-i0-1))>sumthresh) break;
  337.         }
  338.         if(i0<i-1) i0=i-1; else i0=i;
  339.         if(i0>n)    i0=n;
  340.         ncontrolpts++;
  341.         controlpts=(float *)realloc(controlpts, sizeof(float)*2*ncontrolpts);
  342.         controlpts[2*(ncontrolpts-1)]=PX(i0);
  343.         controlpts[2*ncontrolpts-1]=PY(i0);
  344.     }
  345.     [view lockFocus];
  346.     PSsetinstance(YES);
  347.     PSnewinstance();
  348.     [self drawControl:NULL:ncontrolpts-1:bsize];
  349.     PSsetinstance(NO);
  350.     PSflushgraphics();
  351.     [view unlockFocus];
  352.     *cp=ncontrolpts-1;
  353.     return self;
  354. }
  355.  
  356. - (void)drawPath
  357. {
  358.     DPSPrintf(DPSGetCurrentContext()," matrix currentmatrix ");
  359.     if(translated)    PStranslate(tx,ty);
  360.     if(rotated)        PSrotate(M_180_PI*phi);
  361.     if(scaled)        PSscale(sx,sy);
  362.     if(data)    DPSPrintf(DPSGetCurrentContext()," /udt { %s } def ", data); else DPSPrintf(DPSGetCurrentContext()," /udt {} def ");
  363.     DPSPrintf(DPSGetCurrentContext(), " /ctrl ");
  364.     if(controlpts) PSsendfloatarray(controlpts, 2*ncontrolpts); else DPSPrintf(DPSGetCurrentContext(), "[ ]");
  365.     DPSPrintf(DPSGetCurrentContext()," def /nctrl %d def %s setmatrix\n", ncontrolpts, methodname);
  366. }
  367.  
  368. - (void)draw:(NXRect *)re
  369. {
  370.     if(!re || NXIntersectsRect(&bounds, re))
  371.     {
  372.     if(globalOutlines)
  373.     {
  374.     PSgsave();
  375.     if(translated)    PStranslate(tx,ty);
  376.     if(rotated)        PSrotate(M_180_PI*phi);
  377.     if(scaled)        PSscale(sx,sy);
  378.     if(data)    DPSPrintf(DPSGetCurrentContext()," /udt { %s } def ", data); else DPSPrintf(DPSGetCurrentContext()," /udt {} def ");
  379.     DPSPrintf(DPSGetCurrentContext(), " /ctrl ");
  380.     if(controlpts) PSsendfloatarray(controlpts, 2*ncontrolpts); else DPSPrintf(DPSGetCurrentContext(), "[ ]");
  381.     DPSPrintf(DPSGetCurrentContext()," def 0 setlinewidth 0 setgray /nctrl %d def %s stroke\n", ncontrolpts, methodname);
  382.     PSgrestore();
  383.     }
  384.     else
  385.     {
  386.     if(transformed)
  387.     {
  388.     PSgsave();
  389.     if(translated)    PStranslate(tx,ty);
  390.     if(rotated)        PSrotate(M_180_PI*phi);
  391.     if(scaled)        PSscale(sx,sy);
  392.     if(data)    DPSPrintf(DPSGetCurrentContext()," /udt { %s } def ", data); else DPSPrintf(DPSGetCurrentContext()," /udt {} def ");
  393.     DPSPrintf(DPSGetCurrentContext(), " /ctrl ");
  394.     if(controlpts) PSsendfloatarray(controlpts, 2*ncontrolpts); else DPSPrintf(DPSGetCurrentContext(), "[ ]");
  395.     DPSPrintf(DPSGetCurrentContext()," def /cl { %g %g %g %g %g %g } def %g setlinewidth /nctrl %d def /cfl { %s } def /cst { %s } def /cp { %s } def %s\n", c1[0], c1[1], c1[2], c2[0], c2[1], c2[2], linewidth, ncontrolpts, fillmethod, strokemethod, methodname, drawingmethod);
  396.     PSgrestore();
  397.     }
  398.     else
  399.     {
  400.     if(data)    DPSPrintf(DPSGetCurrentContext()," /udt { %s } def ", data); else DPSPrintf(DPSGetCurrentContext()," /udt {} def ");
  401.     DPSPrintf(DPSGetCurrentContext(), " /ctrl ");
  402.     if(controlpts) PSsendfloatarray(controlpts, 2*ncontrolpts); else DPSPrintf(DPSGetCurrentContext(), "[ ]");
  403.     DPSPrintf(DPSGetCurrentContext()," def /cl { %g %g %g %g %g %g } def %g setlinewidth /nctrl %d def /cfl { %s } def /cst { %s } def /cp { %s } def %s\n", c1[0], c1[1], c1[2], c2[0], c2[1], c2[2], linewidth, ncontrolpts, fillmethod, strokemethod, methodname, drawingmethod);
  404.     }
  405.     }
  406.     }
  407. }
  408.  
  409. - calculateBoundingBox:(id)view
  410. {
  411.     float llx, lly, urx, ury;
  412.  
  413.     if(view) [view lockFocus];
  414.     PSgsave();
  415.     if(data)    DPSPrintf(DPSGetCurrentContext()," /udt { %s } def ", data); else DPSPrintf(DPSGetCurrentContext()," /udt {} def ");
  416.     DPSPrintf(DPSGetCurrentContext(), " /ctrl ");
  417.     if(controlpts) PSsendfloatarray(controlpts, 2*ncontrolpts); else DPSPrintf(DPSGetCurrentContext(), "[ ]");
  418.     DPSPrintf(DPSGetCurrentContext()," def /cl { %g %g %g %g %g %g } def /linw %g def /nctrl %d def /cfl { %s } def /cst { %s } def /cp { %s } def %sBB\n", c1[0], c1[1], c1[2], c2[0], c2[1], c2[2], linewidth, ncontrolpts, fillmethod, strokemethod, methodname, drawingmethod);
  419.     PSgetfloat(&ury);
  420.     PSgetfloat(&urx);
  421.     PSgetfloat(&lly);
  422.     PSgetfloat(&llx);
  423.     PSgrestore();
  424.     if(view) [view unlockFocus];
  425.     bounds.origin.x=TRANSFORMX(llx,lly);
  426.     bounds.origin.y=TRANSFORMY(llx,lly);
  427.     bounds.size.width=bounds.size.height=0;
  428.     expandRect(&bounds, TRANSFORMX(llx,ury),TRANSFORMY(llx,ury));
  429.     expandRect(&bounds, TRANSFORMX(urx,ury), TRANSFORMY(urx,ury));
  430.     expandRect(&bounds, TRANSFORMX(urx,lly),TRANSFORMY(urx,lly));
  431.     return self;
  432. }
  433.  
  434. - giveBounds:(NXRect *)bnd
  435. {
  436.     *bnd=bounds;
  437.     return self;
  438. }
  439.  
  440. - (void)drawControl:(NXRect *)re:(int)cp:(float)bsize
  441. {
  442.     [self draw:re];
  443.     if(transformed)    {    PSgsave();
  444.     if(translated)    PStranslate(tx,ty);
  445.     if(rotated)        PSrotate(M_180_PI*phi);
  446.     if(scaled)        PSscale(sx,sy);
  447.     }
  448.     bsize/=sx;
  449.     DPSPrintf(DPSGetCurrentContext()," /bsz %g def /bszh %g def /cctrl %d def %scontrol\n", bsize, bsize/2, cp, methodname);
  450.     if(transformed)    PSgrestore();
  451. }
  452.  
  453. - initWithSettings:(char *)name:(NXColor)co1:(NXColor)co2:(float)lw:(char *)dm:(char *)fm:(char *)sm:(char *)ud
  454. {
  455.     [self setMethodname:name];
  456.     [self setColor1:co1];
  457.     [self setColor2:co2];
  458.     [self setLineWidth:lw];
  459.     [self setDrawingMethod:dm];
  460.     [self setFillMethod:fm];
  461.     [self setStrokeMethod:sm];
  462.     [self setSpecialAttributes:ud];
  463.     ncontrolpts=0;
  464.     sx=sy=1;
  465.     transformed=NO;
  466.     return self;
  467. }
  468.  
  469. // to be called for convertRTFtoCharPath:
  470. - initWithControlPt: (float)x:(float)y
  471. {
  472.     ncontrolpts=1;
  473.     if(controlpts) free(controlpts);
  474.     controlpts=(float *)malloc(sizeof(float)*2);
  475.     controlpts[0]=x;
  476.     controlpts[1]=y;
  477.     sx=sy=1;
  478.     transformed=NO;
  479.     return self;
  480. }
  481.  
  482. - giveSettings:(char **)name:(NXColor *)co1:(NXColor *)co2:(float *)lw:(char **)dmeth:(char **)fillmeth:(char **)strokemeth:(char **)ud
  483. {
  484.     *name=methodname;
  485.     *co1=NXConvertRGBToColor(c1[0],c1[1],c1[2]);
  486.     *co2=NXConvertRGBToColor(c2[0],c2[1],c2[2]);
  487.     *lw=linewidth;
  488.     *dmeth=drawingmethod; *strokemeth=strokemethod; *fillmeth=fillmethod; *ud=data; return self;
  489. }
  490. - setMethodname:(char*)name { if(methodname) free(methodname); methodname=(char *)malloc(sizeof(char)*(strlen(name)+1)); strcpy(methodname, name); return self; }
  491. - setDrawingMethod:(char *)name { if(drawingmethod) free(drawingmethod); drawingmethod=(char *)malloc(sizeof(char)*(strlen(name)+1)); strcpy(drawingmethod, name); return self; }
  492. - setStrokeMethod:(char *)name  { if(strokemethod) free(strokemethod); strokemethod=(char *)malloc(sizeof(char)*(strlen(name)+1)); strcpy(strokemethod, name); return self; }
  493. - setSpecialAttributes:(char *)name  { if(data) free(data); data=(char *)malloc(sizeof(char)*(strlen(name)+1)); strcpy(data, name); return self; }
  494. - setFillMethod:(char *)name  { if(fillmethod) free(fillmethod); fillmethod=(char *)malloc(sizeof(char)*(strlen(name)+1)); strcpy(fillmethod, name); return self; }
  495.  
  496. - setColor1:(NXColor)col { NXConvertColorToRGB(col, &c1[0], &c1[1], &c1[2]); return self; }
  497. - setColor2:(NXColor)col { NXConvertColorToRGB(col, &c2[0], &c2[1], &c2[2]); return self; }
  498. - setLineWidth:(float)lw { linewidth=lw; return self; }
  499. - addTranslation:(float)dtx:(float)dty
  500. {
  501.     tx+=dtx;    ty+=dty;
  502.     if(pow(tx,2)+pow(ty,2)<=1e-6)    {    tx=ty=0; translated=NO; }
  503.         else translated=YES;
  504.     transformed=(translated || scaled || rotated) ? YES:NO;
  505.     return self;
  506. }
  507.  
  508. - centerAt:(NXPoint *)c
  509. {
  510.     return [self addTranslation:(c->x-bounds.origin.x-bounds.size.width/2):(c->y-bounds.origin.y-bounds.size.height/2)];
  511. }
  512.  
  513. - free
  514. {
  515.     if(controlpts) free(controlpts);
  516.     return [super free];
  517. }
  518.  
  519. #define X(a) controlpts[(a)*2]
  520. #define Y(a) controlpts[(a)*2+1]
  521.  
  522. - insertNextPoint:(int *)cp
  523. {
  524.     if(controlpts)
  525.     {
  526.         if(*cp>=0 && *cp<ncontrolpts)
  527.         {
  528.             int i;
  529.  
  530.             controlpts=(float *)realloc(controlpts, sizeof(float)*(ncontrolpts+=2)*2);
  531.             for(i=ncontrolpts-1;i>*cp+1;i--) { X(i)=X(i-2);  Y(i)=Y(i-2); }
  532.             *cp+=2;
  533.             if(*cp<ncontrolpts-3)
  534.             {    X(*cp)=(X(*cp-2)+X(*cp+2))/2; Y(*cp)=(Y(*cp+2)+Y(*cp-2))/2;
  535.                 X(*cp+1)=(X(*cp-1)+X(*cp+3))/2; Y(*cp+1)=(Y(*cp-1)+Y(*cp+3))/2; }
  536.             else
  537.             {
  538. X(*cp)=(X(*cp-2)+X(0))/2; Y(*cp)=(Y(0)+Y(*cp-2))/2;
  539.                 X(*cp+1)=(X(*cp-1)+X(1))/2; Y(*cp+1)=(Y(*cp-1)+Y(1))/2;
  540.             }
  541.         }
  542.     }
  543.     return self;
  544. }
  545.  
  546. - insertPoint:(int *)cp
  547. {
  548.     if(controlpts)
  549.     {
  550.         if(*cp>=0 && *cp<ncontrolpts)
  551.         {
  552.             int i;
  553.  
  554.             controlpts=(float *)realloc(controlpts, sizeof(float)*(++ncontrolpts)*2);
  555.             for(i=ncontrolpts-1;i>*cp;i--) { X(i)=X(i-1);  Y(i)=Y(i-1); }
  556.             ++*cp;
  557.             if(*cp<ncontrolpts-1)
  558.             {    X(*cp)=(X(*cp-1)+X(*cp+1))/2; Y(*cp)=(Y(*cp+1)+Y(*cp-1))/2; }
  559.             else
  560.             {    X(*cp)=(X(*cp-1)+X(0))/2; Y(*cp)=(Y(*cp-1)+Y(0))/2; }
  561.         }
  562.     }
  563.     return self;
  564. }
  565.  
  566. - deletePoint:(int *)cp
  567. {
  568.     if(controlpts)
  569.     {
  570.         if(*cp>=0 && *cp<ncontrolpts && ncontrolpts>0)
  571.         {
  572.             int i;
  573.  
  574.             if(ncontrolpts>1)
  575.             {
  576.                 for(i=*cp;i<ncontrolpts-1;i++) { X(i)=X(i+1);  Y(i)=Y(i+1); }
  577.                 controlpts=(float *)realloc(controlpts, sizeof(float)*(--ncontrolpts)*2);
  578.             }
  579.             else
  580.             { return self; }
  581.             --*cp;
  582.             if(*cp<0)    *cp=0;
  583.         }
  584.     }
  585.     return self;
  586. }
  587.  
  588. // by Ralf Suckow for Bezier curves
  589. // added to Pencil: 26_10_94
  590.  
  591. - insertThreePoints:(int *)cp
  592. {
  593.     if(controlpts)
  594.     {
  595.         if(*cp>=0 && *cp<ncontrolpts)
  596.         {
  597.             int i;
  598.  
  599.             // go to a point on the path
  600.             
  601.             if (*cp % 3 == 1)
  602.               (*cp)--;
  603.             else if (*cp % 3 == 2)
  604.               (*cp)++;
  605.             
  606.             if (*cp == ncontrolpts)
  607.               *cp = 0;
  608.               
  609.             ncontrolpts += 3;
  610.             controlpts = (float *) realloc (controlpts, sizeof (float) * (ncontrolpts) * 2);
  611.             for (i = ncontrolpts - 1; i > *cp + 1; i--) {
  612.               X (i) = X (i-3); 
  613.               Y (i) = Y (i-3);
  614.             }
  615.             
  616.             *cp += 3; // *cp is now the new point on the path,
  617.                       // *cp - 1 and *cp + 1 are the rulers
  618.             
  619.             if ( *cp < ncontrolpts - 3) {
  620.                 
  621.                 X (*cp    ) = (X (*cp - 3) + X (*cp + 3)) / 2;
  622.                 Y (*cp    ) = (Y (*cp - 3) + Y (*cp + 3)) / 2;
  623.                 X (*cp - 1) = (X (*cp - 3) + X (*cp    )) / 2;
  624.                 Y (*cp - 1) = (Y (*cp - 3) + Y (*cp    )) / 2; 
  625.                 X (*cp + 1) = (X (*cp    ) + X (*cp + 3)) / 2;
  626.                 Y (*cp + 1) = (Y (*cp    ) + Y (*cp + 3)) / 2;
  627.             }
  628.             else {
  629.                
  630.                 X (*cp    ) = (X (*cp - 3) + X (0  )) / 2;
  631.                 Y (*cp    ) = (Y (*cp - 3) + Y (0  )) / 2;
  632.                 X (*cp - 1) = (X (*cp - 3) + X (*cp)) / 2;
  633.                 Y (*cp - 1) = (Y (*cp - 3) + Y (*cp)) / 2; 
  634.                 X (*cp + 1) = (X (*cp    ) + X (0  )) / 2;
  635.                 Y (*cp + 1) = (Y (*cp    ) + Y (0  )) / 2;
  636.             }
  637.         }
  638.     }
  639.     return self;
  640. }
  641.  
  642. - alignThreePoints:(int *)cp
  643. {
  644.     int fixed1, fixed2, variable;
  645.     float ffx, fvx, ffy, fvy, base, scale;
  646.  
  647.     if (!controlpts || *cp < 0 || *cp >= ncontrolpts || ncontrolpts < 3)
  648.       return self;
  649.  
  650.     variable = *cp; // moving the selected point
  651.       
  652.     // look which points not to move
  653.           
  654.     if (*cp % 3 == 1) {
  655.         fixed1 = *cp - 1;
  656.         fixed2 = *cp - 2;
  657.     }
  658.     else if (*cp % 3 == 2) {
  659.         fixed1 = *cp + 1;
  660.         fixed2 = *cp + 2;
  661.     }
  662.     else {
  663.         fixed1 = *cp - 1;
  664.         fixed2 = *cp + 1;
  665.     }
  666.     
  667.     fixed1 = (fixed1 + ncontrolpts) % ncontrolpts; // wrap around
  668.     fixed2 = (fixed2 + ncontrolpts) % ncontrolpts;
  669.       
  670.     ffx = X (fixed2) - X (fixed1);
  671.     fvx = X (variable) - X (fixed1);
  672.     ffy = Y (fixed2) - Y (fixed1);
  673.     fvy = Y (variable) - Y (fixed1);
  674.     base = ffx * ffx + ffy * ffy;
  675.       
  676.     if (base < 0.000001) {
  677.         X (variable) = X (fixed1);
  678.         Y (variable) = Y (fixed1);
  679.     }
  680.     else {
  681.         scale = (ffx * fvx + ffy * fvy) / base;
  682.         X (variable) = X (fixed1) + scale * ffx;
  683.         Y (variable) = Y (fixed1) + scale * ffy;
  684.     }
  685.     return self;
  686. }
  687.  
  688. - write:(NXTypedStream *)stream
  689. {
  690.     [super write:stream];
  691.     NXWriteTypes(stream,"*****ffffffffffffcccci", &methodname, &drawingmethod, &strokemethod, &fillmethod, &data, &c1[0], &c1[1], &c1[2], &c2[0], &c2[1], &c2[2], &linewidth, &tx, &ty, &phi, &sx, &sy, &transformed, &rotated, &translated, &scaled, &ncontrolpts);
  692.     NXWriteRect(stream, &bounds);
  693.     NXWriteArray(stream, "f", ncontrolpts*2, controlpts);
  694.     return self;
  695. }
  696.  
  697. - read:(NXTypedStream *)stream
  698. {
  699.     [super read:stream];
  700.      if(NXTypedStreamClassVersion(stream, "PencilGraphic")<1) 
  701.      {
  702.         NXReadTypes(stream,"*****fffffffffffcccci", &methodname, &drawingmethod, &strokemethod, &fillmethod, &data, &c1[0], &c1[1], &c1[2], &c2[0], &c2[1], &c2[2], &linewidth, &tx, &ty, &phi, &sx, &transformed, &rotated, &translated, &scaled, &ncontrolpts);
  703.         sy=sx;
  704.     }
  705.     else
  706.         NXReadTypes(stream,"*****ffffffffffffcccci", &methodname, &drawingmethod, &strokemethod, &fillmethod, &data, &c1[0], &c1[1], &c1[2], &c2[0], &c2[1], &c2[2], &linewidth, &tx, &ty, &phi, &sx, &sy, &transformed, &rotated, &translated, &scaled, &ncontrolpts);
  707.  
  708.     NXReadRect(stream, &bounds);
  709.     controlpts=(float *)malloc(sizeof(float)*2*ncontrolpts);
  710.     NXReadArray(stream, "f", ncontrolpts*2, controlpts);
  711.     return self;
  712. }
  713.  
  714. - (void)writeType:(NXStream *)to
  715. {
  716.     NXPrintf(to,"0");
  717. }
  718.  
  719. #define WRITE_STRING(a) if((a) && strcmp(a,"")) NXPrintf(to, "{%s} ", a); else NXPrintf(to, "{} ")
  720.  
  721. - (void)writeDescription:(NXStream *)to
  722. {
  723.     int i;
  724.     
  725.     [self writeType:(NXStream *)to];
  726.     NXPrintf(to, " { ");
  727.     WRITE_STRING(methodname);
  728.     WRITE_STRING(drawingmethod);
  729.     WRITE_STRING(strokemethod);
  730.     WRITE_STRING(fillmethod);
  731.     WRITE_STRING(data);
  732.     NXPrintf(to, "%g %g %g %g %g %g %g %g %g %g %g %g %d } { %g %g %g %g } { ", c1[0], c1[1], c1[2], c2[0], c2[1], c2[2], linewidth, tx, ty, phi, sx, sy, ncontrolpts,bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
  733.     for(i=0;i<ncontrolpts;i++) { NXPrintf(to, "%g %g ", controlpts[i*2], controlpts[i*2+1]); }
  734.     NXPrintf(to, "}");
  735. }
  736.  
  737. - (char *)giveDescription
  738. {
  739.     int len;
  740.     int maxlen;
  741.     char *buf;
  742.      
  743.     if(result) NXCloseMemory(result, NX_TRUNCATEBUFFER);
  744.     result=NXOpenMemory(NULL,0,NX_WRITEONLY);
  745.     [self writeDescription:result];
  746.     NXGetMemoryBuffer(result, &buf, &len, &maxlen);
  747.     return buf;
  748. }
  749.  
  750. #define NEWSTRING(a) read_brace_string(from, tmp); if(a) free(a); a=(char *)malloc(sizeof(char)*(strlen(tmp)+1)); strcpy(a,tmp)
  751.  
  752. - (void)initFromDescription:(NXStream *)from
  753. {
  754.     static char tmp[500];
  755.     int i;
  756.     
  757.     NXScanf(from," ");
  758.     NXGetc(from);    // the {
  759.     NEWSTRING(methodname);
  760.     NEWSTRING(drawingmethod);
  761.     NEWSTRING(strokemethod);
  762.     NEWSTRING(fillmethod);
  763.     NEWSTRING(data);
  764.     free(controlpts);
  765.     ncontrolpts=0;
  766.     controlpts=NULL;
  767.     NXScanf(from," %f %f %f %f %f %f %f %f %f %f %f %f %d", &c1[0], &c1[1], &c1[2], &c2[0], &c2[1], &c2[2], &linewidth, &tx, &ty, &phi, &sx, &sy, &ncontrolpts);
  768.     NXScanf(from," ");
  769.     NXGetc(from);
  770.     NXScanf(from," ");
  771.     NXGetc(from);
  772.     NXScanf(from, " %f %f %f %f", &bounds.origin.x, &bounds.origin.y, &bounds.size.width, &bounds.size.height);
  773.     NXScanf(from," ");
  774.     NXGetc(from);
  775.     NXScanf(from," ");
  776.     NXGetc(from);
  777.     controlpts=(float *)malloc(sizeof(float)*2*ncontrolpts);
  778.     for(i=0;i<ncontrolpts;i++) { NXScanf(from, " %f %f", &controlpts[i*2], &controlpts[i*2+1]); }
  779.     NXScanf(from," ");
  780.     NXGetc(from);
  781.     if(fabs(phi)<=1e-6) { phi=0; rotated=NO; } else { rotated=YES; }
  782.     if(hypot(tx,ty)<=1e-6) { tx=ty=0; translated=NO; } else { translated=YES; }
  783.     if(fabs(sx-1.0)+fabs(sy-1.0)<1e-6) { sx=sy=1; scaled=NO; } else { scaled=YES; }
  784.     transformed=(translated || scaled || rotated) ? YES:NO;
  785. }
  786.  
  787. - select:(BOOL)yesno { selected=yesno; return self; }
  788. - (BOOL)selected { return (BOOL)selected; }
  789. - (void)drawIfNeeded:(NXRect *)re:(int)cp:(float)bsize
  790. { } // only used for redrawing EPS after a move...
  791. @end